package util

import (
	"time"

	"github.com/astaxie/beego"
	memory "github.com/patrickmn/go-cache"
)

type Cache interface {
	New() Cache
	Set(k string, v interface{})
	Get(k string) (interface{}, bool)
	Flush()
	Delete(k string)
}

const (
	InMemory int = iota
	Remote
)

var cacheMap = NewSafeMap()

func CacheType() int {
	return beego.AppConfig.DefaultInt("cacheType", 0)
}

func NewCache(name string, cacheType int, expiration time.Duration) (c Cache) {
	if cacheType == InMemory {
		mc := &InMemoryCache{Name: name, Expiration: expiration, CleanUpInterval: -1}
		//mc := &InMemoryCache{Name: name, Expiration: expiration, CleanUpInterval: 30 * time.Second}
		c = mc.New()
		cacheMap.Set(name, c)
	} else {
	}
	return
}

func NewCacheL(name string, cacheType int, loaders ...ValueReloader) (c Cache) {
	c = NewCache(name, cacheType, time.Duration(-1))
	for _, l := range loaders {
		l.c = c
		l.start()
	}
	return
}

func GetCache(name string) (c Cache, found bool) {
	var e interface{}
	if e, found = cacheMap.Get(name); found {
		c = e.(Cache)
	}
	return
}

type ValueReloader struct {
	Fn       func() interface{}
	Interval time.Duration
	Key      string
	c        Cache
}

func (r *ValueReloader) start() {
	go func(key string, fn func() interface{}, d time.Duration) {
		t1 := time.NewTimer(d)
		for {
			select {
			case <-t1.C:
				r.c.Set(key, fn())
				t1.Reset(d)
			}
		}
	}(r.Key, r.Fn, r.Interval)
}

type InMemoryCache struct {
	Name            string
	Entry           *memory.Cache
	Expiration      time.Duration
	CleanUpInterval time.Duration
}

func (m *InMemoryCache) New() Cache {
	m.Entry = memory.New(m.Expiration, m.CleanUpInterval)
	return m
}

func (m *InMemoryCache) Set(k string, v interface{}) {
	m.Entry.Set(k, v, memory.DefaultExpiration)
}

func (m *InMemoryCache) Get(k string) (interface{}, bool) {
	return m.Entry.Get(k)
}

func (m *InMemoryCache) Delete(k string) {
	m.Entry.Delete(k)
}

func (m *InMemoryCache) Flush() {
	m.Entry.Flush()
}
